/*************************************************************************
 *  Copyright (C), 2017-2018, Mogoson tech. Co., Ltd.
 *  FileName: AreaRoam.cs
 *  Author: Mogoson   Version: 1.0   Date: 7/11/2017
 *  Version Description:
 *    Internal develop version,mainly to achieve its function.
 *  File Description:
 *    Ignore.
 *  Class List:
 *    <ID>           <name>             <description>
 *     1.           AreaRoam               Ignore.
 *  Function List:
 *    <class ID>     <name>             <description>
 *     1.
 *  History:
 *    <ID>    <author>      <time>      <version>      <description>
 *     1.     Mogoson     7/11/2017       1.0        Build this file.
 *************************************************************************/

namespace Developer.AreaRoam
{
    using AnimationCurve;
    using System.Collections;
    using UnityEngine;

    [AddComponentMenu("Developer/AreaRoam/AreaRoam")]
    public class AreaRoam : Roam
    {
        #region Property and Field
        /// <summary>
        /// Area of roam.
        /// </summary>
        public Area area;

        /// <summary>
        /// Time of path curve.
        /// </summary>
        protected float time = 0;

        /// <summary>
        /// Delta to calculate tangent of point on path curve.
        /// </summary>
        protected float delta = 0.01f;

        /// <summary>
        /// Max count of curve's key frames.
        /// </summary>
        protected int maxLength = 20;

        /// <summary>
        /// Threshold to clear curve.
        /// </summary>
        protected int threshold = 10;

        /// <summary>
        /// Curve of roam path.
        /// </summary>
        protected VectorAnimationCurve curve;
        #endregion

        #region Protected Method
        protected virtual void Start()
        {
            StartCoroutine(ExtendCurve());
        }

        protected virtual void Update()
        {
            RoamArea();
        }

        protected virtual void OnDrawGizmosSelected()
        {
            if (curve == null)
                return;

            Gizmos.color = new Color(0, 1, 1, 1);
            for (float t = time; t < curve[Mathf.Min(threshold, curve.length - 1)].time; t += 0.05f)
            {
                Gizmos.DrawLine(curve.Evaluate(t), curve.Evaluate(t + 0.05f));
            }
        }

        /// <summary>
        /// Extend path curve.
        /// </summary>
        protected virtual IEnumerator ExtendCurve()
        {
            //Initialise curve.
            var lastPoint = transform.position;
            curve = new VectorAnimationCurve();
            curve.AddKey(0, lastPoint);

            //Extend curve.
            while (true)
            {
                if (curve.length < maxLength)
                {
                    var newPoint = area.GetPoint();
                    while (newPoint == lastPoint)
                    {
                        yield return new WaitForSeconds(Time.deltaTime);
                        newPoint = area.GetPoint();
                    }
                    curve.AddKey(curve[curve.length - 1].time + Vector3.Distance(newPoint, lastPoint), newPoint);
                    lastPoint = newPoint;
                }
                yield return new WaitForSeconds(Time.deltaTime);
            }
        }

        /// <summary>
        /// Clear path curve [Remove the used points].
        /// </summary>
        protected void ClearCurve()
        {
            var keyFrames = new VectorKeyframe[curve.length - threshold + 2];
            for (int i = 0; i < keyFrames.Length; i++)
            {
                keyFrames[i] = curve[threshold - 2 + i];
            }

            curve = new VectorAnimationCurve();
            for (int i = 0; i < keyFrames.Length; i++)
            {
                curve.AddKey(keyFrames[i].time - keyFrames[1].time, keyFrames[i].value);
            }
        }

        /// <summary>
        /// Tow transform base on path curve.
        /// </summary>
        /// <param name="t">Time of path curve.</param>
        protected void TowTransformBaseOnCurve(float t)
        {
            var pathPoint = curve.Evaluate(t);
            var tangent = (curve.Evaluate(t + delta) - pathPoint).normalized;

            //Update position and look at tangent.
            transform.position = pathPoint;
            transform.LookAt(pathPoint + tangent, Vector3.up);
        }

        /// <summary>
        /// Roam the area.
        /// </summary>
        protected virtual void RoamArea()
        {
            if (curve.length < 3 || time >= curve[curve.length - 2].time)
                return;

            if (curve.length >= threshold && time >= curve[threshold - 1].time)
            {
                time = 0;
                ClearCurve();
            }

            time += speed * Time.deltaTime;
            TowTransformBaseOnCurve(time);
        }
        #endregion
    }//class_end
}//namespace_end